/*
 * ContextTable.cpp
 *
 *  Created on: 27/11/2009
 *      Author: ngonzalez
 */

#include "ContextTable.h"

ContextTable::ContextTable() {
}

ContextTable::ContextTable(int cantDeCaracteresPosibles) {

	this->divisorInicial = cantDeCaracteresPosibles;

}


ContextTable::~ContextTable() {

	while (!this->contextos.empty()){

		deListas *lista;

		lista = this->contextos.front();

		delete lista->contexto;

		this->contextos.pop_front();

		delete lista;

	}

}

bool ContextTable::existInListaCaracteresYaExistentes (char caracterABuscar){

	list <char>::iterator it;

	it = this->caracteresYaExistentes.begin();


	for (;it != this->caracteresYaExistentes.end();it++){


		if ( (*it) == caracterABuscar) return true;
	}

	return false;

}

void ContextTable::updateTable(char contexto, char caracter){

	bool encontro;

	list <deListas*>::iterator it2;

	encontro = existInListaCaracteresYaExistentes (contexto);


	// SI encontro significa que el contexto ya tenia algun caracter registrado

	if (encontro == true)
	{

		list <NodoListaContexto>::iterator  it3;

		it2 = this->contextos.begin();

		// Se busca la tabla de caracteres registrados para ese contexto

		for (; it2 != this->contextos.end(); it2++){


			if ((*it2)->caracter==contexto){

				it3 =(*it2)->contexto->begin();

				// Si se encontro la tabla de contextos, se busca si el contexto ya habia sido registrado para aumentar en 1 su frecuencia

				for (; it3 != (*it2)->contexto->end();it3++){

					if (it3->caracter == caracter){

						it3->frecuencia++;

						(*it2)->divisor++;

						return;
					}

				}

				// Si no encontro la tabla de contextos, se agrega un nuevo contexto con frecuencia = 2 (por default todos tienen frecuencia 1)
				NodoListaContexto nodo;

				nodo.caracter = caracter;

				nodo.frecuencia = 2;

				(*it2)->contexto->push_front(nodo);

				(*it2)->divisor++;

				return;
			}

		}


		throw string("No se encontro tabla de contextos para un caracter que deberia tenerla");

		return;

	}

	// No encontro el caracter en la lista de caracteres ya existentes. Se creara una tabla de contextos

	this->caracteresYaExistentes.push_front(contexto);

	deListas *listaDeContextos = new deListas();

	listaDeContextos->caracter = contexto;

	list <NodoListaContexto> *listaNueva = new list <NodoListaContexto>;

	listaDeContextos->contexto = listaNueva;

	NodoListaContexto nuevoNodo;

	nuevoNodo.caracter = caracter;

	nuevoNodo.frecuencia = 2;

	listaDeContextos->contexto->push_back(nuevoNodo);

	listaDeContextos->divisor = this->divisorInicial + 1;

	this->contextos.push_back(listaDeContextos);

	return;

}

double ContextTable::getProbability(char contexto, char caracter){

	float uno = 1;
	float divisor;
	double resultado;

	divisor = this->divisorInicial;


	if (!this->existInListaCaracteresYaExistentes(contexto)){


		resultado = uno / divisor;

		return resultado;
	}

	list <deListas*>::iterator it2;

	list <NodoListaContexto>::iterator  it3;

	it2 = this->contextos.begin();

	// Se busca la tabla de caracteres registrados para ese contexto

	for (; it2 != this->contextos.end(); it2++){


		if ((*it2)->caracter==contexto){

			it3 = (*it2)->contexto->begin();


			// Si se encontro la tabla de caracteres, se busca si el caracter ya habia sido registrado y se devuelve su probabilidad

			for (; it3 != (*it2)->contexto->end();it3++){

				if (it3->caracter == caracter){


					double frecuenciaAux = it3->frecuencia;

					double divisorAux = (*it2)->divisor;

					resultado = (frecuenciaAux /divisorAux);

					return resultado;
				}

			}

			// Si el contexto no existe, por default se devuelve 1 / cant de caracteres

			double divisorAux1 = (*it2)->divisor;

		    resultado = uno / divisorAux1;

		    return resultado;

	    }



    }

	throw string("No se encontro tabla de contextos para un caracter que deberia tenerla");

	return -1;

}


int ContextTable::getFrecuency(char contexto, char caracter){

	if (!this->existInListaCaracteresYaExistentes(contexto)){

		return 1;
	}

	list <deListas*>::iterator it2;

	list <NodoListaContexto>::iterator  it3;

	it2 = this->contextos.begin();

	// Se busca la tabla de caracteres registrados para ese contexto

	for (; it2 != this->contextos.end(); it2++){


			if ((*it2)->caracter==contexto){

				it3 = (*it2)->contexto->begin();


				// Si se encontro la tabla de caracteres, se busca si el caracter ya habia sido registrado y se devuelve su probabilidad

				for (; it3 != (*it2)->contexto->end();it3++){

					if (it3->caracter == caracter){

							return it3->frecuencia;

						}

				}
				return 1;

			}
		}

	throw string("No se encontro tabla de contextos para un caracter que deberia tenerla");

	return -1;


}

int    ContextTable::getDivisor (char contexto){

	if (!this->existInListaCaracteresYaExistentes(contexto)){

		return this->divisorInicial;
	}

	list <deListas*>::iterator it2;

	it2 = this->contextos.begin();

		// Se busca la tabla de caracteres registrados para ese contexto

	for (; it2 != this->contextos.end(); it2++){

		if ((*it2)->caracter==contexto){

			return (*it2)->divisor;
		}
	}

	throw string("No se encontro tabla de contextos para un caracter que deberia tenerla");

	return -1;
}

double ContextTable::getAcummulatedProbability (char contexto, char caracter){

	int askii = static_cast<int>(caracter);

	int frecuencia;

	int frecuenciaTotal = 0;

	for (int i=0;i <= askii;i++){

		char aux;

		aux = static_cast<char>(i);

		frecuencia = this->getFrecuency(contexto,aux);

		frecuenciaTotal = frecuenciaTotal + frecuencia;

//		cout << "Frecuencia Total : " << frecuenciaTotal << endl;

	}

	int divisor = this->getDivisor(contexto);

//	cout << "Divisor " << divisor << endl;

	if (divisor == -1 ) return -1;

	double resultado, divisorDouble, frecuenciaDouble;

	divisorDouble = divisor;

	frecuenciaDouble = frecuenciaTotal;

	resultado = frecuenciaDouble / divisorDouble;


	return resultado;
}


char ContextTable::getCharByAcummulatedProbability (double acummulatedProbability, char contexto){

		int frecuencia;

		int frecuenciaTotal = 0;

		double divisor = this->getDivisor(contexto);

		if (divisor == -1 ) return ' ';

		cout << "Divisor " << divisor << endl;

		for (int i=0;i <= 255;i++){

			char aux;

			aux = static_cast<char>(i);

			frecuencia = this->getFrecuency(contexto,aux);

			frecuenciaTotal = frecuenciaTotal + frecuencia;

			cout << "Frecuencia Total : " << frecuenciaTotal << endl;

			double resultado, frecuenciaDouble;

			frecuenciaDouble = frecuenciaTotal;

			resultado = frecuenciaDouble / divisor;

			if (resultado >= acummulatedProbability){

				return aux;
			}
		}


	return ' ';


}
